package com.nvwa.mvp.validated.advice;

import com.nvwa.coding.util.safe.Coding;
import com.nvwa.dto.err.DefErrorCodeEnum;
import com.nvwa.dto.resp.Response;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import lombok.extern.slf4j.Slf4j;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@RestControllerAdvice
public class ExceptionControllerAdvice {
    /**
     * @Validated 校验参数未通过抛出的异常处理
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    public Response methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        BindingResult br = e.getBindingResult();
        if (br.hasFieldErrors()) {
            List<String> errors = Coding.stream(br.getFieldErrors())
                    .map(err -> err.getField() + ":" + err.getDefaultMessage())
                    .collect(Collectors.toList());
            return Response.error(DefErrorCodeEnum.PARAM_ERROR.with(errors.toString()));
        }
        return Response.error(DefErrorCodeEnum.PARAM_ERROR);
    }

    /**
     * 普通参数(非 java bean)校验出错
     * 校验参数 @RequestParam @PathVariable 时的异常
     * 注意 @Validated 需要放在controller的类上
     * 例如：
     *
     * @RestController
     * @RequestMapping("/cs")
     * @Validated public class TestController {
     * @PostMapping("/test") public Results test(
     * @Pattern(regexp = "^\\d{19}$", message = "用户ID，应为19位数字") String id,
     * @NotBlank(message = "名字不能为空") String name
     * ) {}
     * }
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ConstraintViolationException.class)
    public Response constraintViolationException(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        if (Coding.isEmpty(violations)) {
            return Response.error(DefErrorCodeEnum.PARAM_ERROR);
        }
        Map<String, String> map = violations.stream()
                .collect(Collectors.toMap(o -> {
                    PathImpl x = (PathImpl) o.getPropertyPath();
                    return x.getLeafNode().toString();
                }, ConstraintViolation::getMessage, (k1, k2) -> k1));
        return Response.error(DefErrorCodeEnum.PARAM_ERROR.with(map.toString()));
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(Exception.class)
    public Response catchException(Exception e) {
        return Response.error(DefErrorCodeEnum.UNKNOW);
    }
}